#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <stdbool.h>
#include <pthread.h>

int g_sfd = 0;
#define PROCESS_NUM 100
#define MAXEVENTS 64

static int createAndBind(int port)
{
	int fd = socket(PF_INET, SOCK_STREAM, 0);

	struct sockaddr_in serveraddr;
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
	serveraddr.sin_port = htons(port);

	bind(fd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr_in));
	return fd;
}

static int makeNonBlocking(int sfd)
{
	int flags = fcntl(sfd, F_GETFL, 0);
	flags |= O_NONBLOCK;
	fcntl(sfd, F_SETFL, flags);

	return 0;
}

static void* _threadFunc(void* data)
{
	int tid = (int)pthread_self();

	int efd = epoll_create(MAXEVENTS);

	struct epoll_event event;
	event.data.fd = g_sfd;
	// event.events = EPOLLIN | EPOLLET;
	// EPOLLEXCLUSIVE 能解决惊群问题 
	event.events = EPOLLIN | EPOLLEXCLUSIVE;

	int ret = epoll_ctl(efd, EPOLL_CTL_ADD, g_sfd, &event);
	if (ret == -1)
	{
		perror("epoll_ctl");
		abort();
	}

	/* Buffer where events are returned */
	struct epoll_event* events = calloc(MAXEVENTS, sizeof(struct epoll_event));

	/* In child: The event loop */
	while (true)
	{
		int n = epoll_wait(efd, events, MAXEVENTS, -1);
		printf("thread %d return from epoll_wait! n = %d\n", tid, n);

		for (int i = 0; i < n; i++)
		{
			if ((events[i].events & EPOLLERR)
				|| (events[i].events & EPOLLHUP)
				|| (!(events[i].events & EPOLLIN)))
			{
				/* An error has occured on this fd, or the socket is not ready for reading(why were we notified then?) */
				fprintf(stderr, "epoll error\n");
				close(events[i].data.fd);
				continue;
			}
			else if (g_sfd == events[i].data.fd)
			{
				/* We have a notification on the listening socket, which means one or more incoming connections. */
				struct sockaddr in_addr;
				char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];

				socklen_t in_len = sizeof(struct sockaddr);
				int infd = accept(g_sfd, &in_addr, &in_len);
				if (infd == -1)
				{
					printf("thread %d accept failed!\n", tid);
					break;
				}
				printf("thread %d accept successed!\n", tid);

				/* Make the incoming socket non-blocking and add it to the list of fds to monitor. */
				close(infd);
			}
		}
	}

	free(events);
	return NULL;
}

int main(int argc, char *argv[])
{
	g_sfd = createAndBind(8003);
	makeNonBlocking(g_sfd);
	listen(g_sfd, SOMAXCONN);

	pthread_t tids[PROCESS_NUM];
	for (int i = 0; i < PROCESS_NUM; i++)
	{
		pthread_create(&tids[i], NULL, _threadFunc, NULL);
	}

	for (int i = 0; i < PROCESS_NUM; i++)
	{
		pthread_join(tids[i], NULL);
	}

	close(g_sfd);
	return 0;
}